﻿using System;
using System.IO;
using System.Text;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using Newtonsoft.Json.Linq;
using TrinityAPI.Base;
using TrinityAPI.Base.Tools;
using TrinityAPI.Base.Models;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading;
using System.Net.WebSockets;
using System.Xml;
using Taobao.Open.Platform;
using Newtonsoft.Json;

namespace Express.CaiNiao
{
    public class GetWaybill : IAction
    {
        string environment = "";
        public JToken Invoke(JObject dataset, params JToken[] args)
        {
            try
            {
                LogHelper.Info(dataset.ToString());
                var constant = dataset.GetJsonObject("constant");
                JObject bodyObject = dataset.GetJsonObject("request");
                //从参数取值
                JObject argsParams = null;
                if (args != null && args.Length > 0)
                {
                    argsParams = args[0] as JObject;
                    if (argsParams != null)
                    {
                        LogHelper.Info(argsParams.ToString());
                    }
                }

                var cainiaoWaybillConfig = constant.GetNecessaryJsonObject("cainiaoWaybillConfig", "constant");
                string path = "constant.cainiaoWaybillConfig";
                string waybillFileDir = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("waybillfiledir", path);
                string printerService = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("printerservice", path);
                string topUrl = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("topUrl", path);
                string stdTemplateurl = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("stdTemplateurl", path);
                string customTemplateurl = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("customTemplateurl", path);
                string topAppkey = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("appkey", path);
                string topSecret = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("secretkey", path);
                string topSessionKey = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("sessionKey", path);
                string userID = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("userID", path);
                string customerCode= cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("customerCode", path);
                string cpCode = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("cpCode", path);
                string waybillBrandCode = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("waybillBrandCode", path);
                string orderChannelsType = cainiaoWaybillConfig.GetNecessaryJsonObjectStringValue("orderChannelsType", path);
                string printer = cainiaoWaybillConfig.GetJsonObjectStringValue("printer");
                string productCode = cainiaoWaybillConfig.GetJsonObjectStringValue("productCode");
                bool isDoCall = cainiaoWaybillConfig.GetJsonObjectBooleanValue("isDoCall");
                if (string.IsNullOrWhiteSpace(productCode))
                {
                    productCode = "2";//默认特惠
                }
                environment = cainiaoWaybillConfig.GetJsonObjectStringValue("environment");
                //请求数据
                string eSONo = bodyObject.GetJsonObjectStringValue("ecom_orderid");
                string orderId = bodyObject.GetJsonObjectStringValue("orderId");
                string from_address = bodyObject.GetJsonObjectStringValue("from_address");
                string from_contact = bodyObject.GetJsonObjectStringValue("from_contact");
                string from_tel = bodyObject.GetJsonObjectStringValue("from_mobile");
                string from_mobile = bodyObject.GetJsonObjectStringValue("from_mobile");
                string from_province = SplitAddress(from_address).GetJsonObjectStringValue("provinceName");
                string from_city = SplitAddress(from_address).GetJsonObjectStringValue("cityName");
                string from_district = SplitAddress(from_address).GetJsonObjectStringValue("countryName");
                string from_detail = SplitAddress(from_address).GetJsonObjectStringValue("address");
                string d_address = bodyObject.GetJsonObjectStringValue("to_address");
                string d_contact = bodyObject.GetJsonObjectStringValue("to_contact");
                string d_tel = bodyObject.GetJsonObjectStringValue("to_phone");
                string d_mobile = bodyObject.GetJsonObjectStringValue("to_mobile");
                string d_province = null;
                string d_city = null;
                string d_country = null;
                string d_detailaddress = null;
                string[] d_addresses = d_address.Trim().Split(' ');
                if (d_addresses.Length > 3)
                {
                    d_province = d_addresses[0];
                    d_city = d_addresses[1];
                    d_country = d_addresses[2];
                    d_detailaddress = d_address.Substring(d_province.Length + d_city.Length + d_country.Length);
                }
                CainiaoWaybillInfo waybillInfo = new CainiaoWaybillInfo();
                waybillInfo.Appkey = topAppkey;
                waybillInfo.Secret = topSecret;
                waybillInfo.SessionKey = topSessionKey;
                waybillInfo.URL = topUrl;
                waybillInfo.UserID = long.Parse(userID);
                waybillInfo.CustomerCode = customerCode;
                waybillInfo.StdTemplateUrl = stdTemplateurl;
                waybillInfo.CustomTemplateUrl = customTemplateurl;
                waybillInfo.Tid = eSONo;
                waybillInfo.OrderID = orderId;
                waybillInfo.ProductCode = productCode;
                waybillInfo.CallDoorPickUp = isDoCall;

                if (argsParams != null)
                {
                    waybillInfo.Oaid = argsParams.GetJsonObjectStringValue("oaid");
                }

                waybillInfo.WaybillBrandCode = waybillBrandCode;
                waybillInfo.OrderChannelsType = orderChannelsType;
                waybillInfo.CpCode = cpCode;
                waybillInfo.SenderProvince = from_province;
                waybillInfo.SenderCity = from_city;
                waybillInfo.SenderDistrict = from_district;
                waybillInfo.SenderDetail = from_detail;
                waybillInfo.SenderName = from_contact;
                waybillInfo.SenderPhone = from_tel;
                waybillInfo.SenderMobile = from_mobile;
                waybillInfo.ReceiverProvince = d_province;
                waybillInfo.ReceiverCity = d_city;
                waybillInfo.ReceiverDistrict = d_country;
                waybillInfo.ReceiverDetail = d_address;
                waybillInfo.ReceiverName = d_contact;
                waybillInfo.ReceiverPhone = d_tel;
                waybillInfo.ReceiverMobile = d_mobile;

                string remark = "";
                List<JObject> cargoes = bodyObject.GetJsonObjectList("cargoes");
                waybillInfo.Items = new List<GoodItem>();
                foreach (JObject row in cargoes)
                {
                    waybillInfo.Items.Add(new GoodItem() { SKU = row.GetJsonObjectStringValue("sku"), Count = row.GetJsonObjectIntValue("qty") });
                    remark = string.Format("{0}*{1};", row.GetJsonObjectStringValue("sku"), row.GetJsonObjectIntValue("qty").ToString());
                }
                waybillInfo.GoodsDescription = "GoodsDescription";
                remark = remark + eSONo;
                //获取打印数据
                string printData = "";
                string waybillCode = "";
                string pdfFileName = "";
                CainiaoPrintAPI printAPI = new CainiaoPrintAPI();
                string waybillXML = printAPI.WaybillGet(waybillInfo);
                //LogHelper.Info(waybillXML);
                XmlDocument waybilldoc = new XmlDocument();
                waybilldoc.LoadXml(waybillXML);
                string waybillJsonStr = JsonConvert.SerializeXmlNode(waybilldoc, Newtonsoft.Json.Formatting.None);
                var waybillJson = JObject.Parse(waybillJsonStr);
                
                var res = waybillJson.GetJsonObject("cainiao_waybill_ii_get_response").GetJsonObject("modules").GetJsonObject("waybill_cloud_print_response");
                if (res != null)
                {
                    waybillCode = res.GetJsonObjectStringValue("waybill_code");
                    printData = res.GetJsonObjectStringValue("print_data");
                    LogHelper.Info(printData);
                }
                else
                {
                    res = waybillJson.GetJsonObject("error_response");
                    string sub_code = res.GetJsonObjectStringValue("sub_code");
                    string sub_msg = res.GetJsonObjectStringValue("sub_msg");
                    LogHelper.Info(res.ToString());
                    throw new BaseException("@E210000002", sub_msg);
                }
                if (!string.IsNullOrEmpty(waybillCode))
                {                  
                    var returnMsg = WaybillPrint(printerService, printData, waybillCode, printer, remark, customTemplateurl, waybillFileDir);                  
                    if (returnMsg.Result != "")
                    {
                        pdfFileName = returnMsg.Result;
                    }
                }
                JObject response = new JObject();
                response["orderId"] = orderId;
                response["waybillCode"] = waybillCode;
                response["waybillFilePath"] = pdfFileName;
                return response;

            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Info("GetWaybillInfo fail: " + ex.ToString());
                throw new Exception("@E210000002");
            }
        }

        #region 菜鸟电子面单
        public async Task<string> WaybillPrint(string serviceUrl, string printdata, string waybillCode, string printer, string remark, string customTemplateUrl, string imagePath)
        {
            string returnMsg = "";
            try
            {
                string printCmd = GetPrintCMD(waybillCode, printer, printdata, remark, customTemplateUrl);
                var response = await WebSocket(serviceUrl, printCmd);
                JObject rs = JObject.Parse(response);
                //LogHelper.Info(rs.ToString());
                if (rs != null && rs.GetJsonObjectStringValue("status") == "success")
                {
                    string taskID = rs.GetJsonObjectStringValue("taskID");
                    bool isSuccess = false;
                    for (int i = 0; i < 300; i++)
                    {
                        string path = Path.Combine(imagePath, taskID + ".pdf");
                        if (System.IO.File.Exists(path))
                        {
                            string base64Image = FileToBase64String(path);
                            string filepath = imagePath;
                            string filename = taskID + "_trinity.pdf";
                            if(string.IsNullOrWhiteSpace(environment))
                            {
                                filename = waybillCode + ".pdf";
                            }
                            GeneratePDF(filepath, filename, base64Image, 100, 180);
                            isSuccess = true;
                            returnMsg = filename;
                            break;
                        }
                        else
                        {
                            Thread.Sleep(100);
                        }
                    }
                    if (!isSuccess)
                    {
                        throw new Exception("生成PDF失败!");
                    }
                }
                else
                {
                    throw new Exception("菜鸟打印服务异常!");
                }
            }
            catch (Exception ex)
            {
                throw new Exception("生成快递单失败!" + ex.Message);
            }
            return returnMsg;
        }
        public async Task<string> WebSocket(string webSocketUrl, string sendMsg)
        {
            try
            {
                var tokenSource = new CancellationTokenSource(10000);
                using (ClientWebSocket webSocket = new ClientWebSocket())
                {
                    var receiveMsg = new StringBuilder();
                    webSocket.ConnectAsync(new Uri(webSocketUrl), tokenSource.Token).Wait(tokenSource.Token);
                    if (webSocket.State == WebSocketState.Open)
                    {
                        byte[] bsend = Encoding.UTF8.GetBytes(sendMsg);
                        webSocket.SendAsync(new ArraySegment<byte>(bsend), WebSocketMessageType.Text, true, CancellationToken.None);
                        ArraySegment<byte> receiveSegment = new ArraySegment<byte>(new byte[1024]);
                        WebSocketReceiveResult receiveResult = new WebSocketReceiveResult(0, WebSocketMessageType.Text, false);
                        while (!receiveResult.EndOfMessage)
                        {
                            receiveResult = webSocket.ReceiveAsync(receiveSegment, CancellationToken.None).Result;
                            receiveMsg.Append(Encoding.UTF8.GetString(receiveSegment.Array, 0, receiveResult.Count));
                        }
                        tokenSource = new CancellationTokenSource(10000);
                        webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "1", tokenSource.Token).Wait(tokenSource.Token);
                    }
                    return receiveMsg.ToString();
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
        public string GetPrintCMD(string waybillCode, string printer, string printData, string remark, string templateUrl)
        {
            List<JObject> jsonContentList = new List<JObject>();
            jsonContentList.Add(JObject.Parse(printData));
            jsonContentList.Add(
                new JObject()
                {
                    { "templateURL", templateUrl },
                    { "data", new JObject()
                        {
                            { "remark" ,remark}
                        }
                    }
                }
            );
            List<JObject> jsonDocList = new List<JObject>();
            JObject jsonDoc = new JObject();
            jsonDoc.Add("documentID", waybillCode);
            jsonDoc.Add("contents", JArray.FromObject(jsonContentList));
            jsonDocList.Add(jsonDoc);
            JObject jsonCMD = new JObject();
            jsonCMD["cmd"] = "print";
            jsonCMD["requestID"] = DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString().Substring(0,8);
            jsonCMD["version"] = "1.0";
            jsonCMD["task"] = new JObject()
            {
                {"taskID",waybillCode+"_"+ DateTime.Now.ToString("yyyyMMddHHmmssfff")+ "_" + Guid.NewGuid().ToString().Substring(0,8) },
                { "preview", false },
                {"printer", printer },
                {"previewType", "pdf" },
                {"firstDocumentNumber", 1 },
                {"totalDocumentCount", 1 },
                {"documents", JArray.FromObject(jsonDocList)}
            };
            return jsonCMD.ToString();
        }
        private void GeneratePDF(string filePath, string filename, string imageData, int templateWidth, int templateHeight)
        {
            if (!System.IO.Directory.Exists(filePath))
            {
                System.IO.Directory.CreateDirectory(filePath);
            }
            using (PdfDocument document = new PdfDocument())
            {
                PdfPage page = document.AddPage();
                page.Size = PdfSharp.PageSize.A4;
                page.Orientation = PdfSharp.PageOrientation.Landscape;
                XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsUnit.Millimeter);
                byte[] arr = Convert.FromBase64String(imageData);
                var ms = new System.IO.MemoryStream(arr);
                using (XImage image = XImage.FromStream(ms))
                {
                    double width = 210;
                    double height = 297;
                    gfx.DrawImage(image, (height / 2 - templateWidth) / 2, (width - templateHeight) / 2, templateWidth, templateHeight);
                    gfx.DrawImage(image, (height - 2 * templateWidth) / 4 + height / 2, (width - templateHeight) / 2, templateWidth, templateHeight);
                    document.Save(Path.Combine(filePath, filename));
                }
                ms.Close();
            }
        }
        public static String XMLDecode(string strData)
        {
            strData = strData.Replace("&lt;", "<");
            strData = strData.Replace("&gt;", ">");
            strData = strData.Replace("&apos;", "'");
            strData = strData.Replace("&quot;", "\"");
            strData = strData.Replace("&amp;", "&");
            return strData;
        }
        public string FileToBase64String(string filePath)
        {
            FileStream fsForRead = new FileStream(filePath, FileMode.Open);//文件路径
            string base64Str = "";
            try
            {
                fsForRead.Seek(0, SeekOrigin.Begin);
                byte[] bs = new byte[fsForRead.Length];
                int log = Convert.ToInt32(fsForRead.Length);
                fsForRead.Read(bs, 0, log);
                base64Str = Convert.ToBase64String(bs);
                return base64Str;
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
                Console.ReadLine();
                return base64Str;
            }
            finally
            {
                fsForRead.Close();
            }
        }

        private static JObject SplitAddress(string address)
        {
            if (address == null)
                address = string.Empty;

            string province = null;
            string city = null;
            string country = null;

            address = address.Trim();
            var matches1 = System.Text.RegularExpressions.Regex.Matches(address, @"^[^省]+省|[^自治区]+自治区|北京市|天津市|上海市|重庆市");
            if (matches1.Count > 0)
            {
                province = matches1[0].Groups[0].Value;
                address = address.Substring(province.Length);
            }
            else
            {
                province = address.Substring(0, 2);
                address = address.Substring(2);
            }

            address = address.Trim();
            var matches2 = System.Text.RegularExpressions.Regex.Matches(address, @"^[^市]+市|[^自治州]+自治州|[^盟]+盟");
            if (matches2.Count > 0)
            {
                city = matches2[0].Groups[0].Value;
                address = address.Substring(city.Length);
            }
            else
            {
                city = address.Substring(0, 2);
                address = address.Substring(2);
            }

            address = address.Trim();

            var matches3 = System.Text.RegularExpressions.Regex.Matches(address, @"^[^县]+县|[^旗]+旗|.+区");
            if (matches3.Count > 0)
            {
                country = matches3[0].Groups[0].Value;
                address = address.Substring(country.Length);
            }
            else
            {
                country = address.Substring(0, 2);
                address = address.Substring(2);
            }

            return new JObject()
            {
                { "provinceName", province },
                { "cityName", city },
                { "countryName", country },
                { "address", address }
            };
        }
        #endregion
    }
}
